package com.swak.vertx.invoker;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

import com.swak.App;
import com.swak.asm.MethodCache;
import com.swak.asm.MethodCache.ClassMeta;
import com.swak.asm.MethodCache.MethodMeta;
import com.swak.asm.Wrapper;
import com.swak.exception.InvokeException;

import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

/**
 * 调用执行器 -- 适用JDK 的动态代理，也适用CGlib 的子类代理
 *
 * @author: lifeng
 * @date: 2020/3/29 20:28
 */
public class FluxInvokerProxy implements InvocationHandler, MethodInterceptor, FluxInvoker {

	private static final String GET_CLASS = "getClass";
	private static final String HASH_CODE = "hashCode";
	private static final String TO_STRING = "toString";
	private static final String EQUALS = "equals";

	private static final Object NONE_LOCAL_SERVICE = new Object();

	private final Class<?> type;
	private final String address;
	private final ClassMeta classMeta;

	/**
	 * 如果使用的非异步接口，则直接使用本地调用，会导致当前方法阻塞
	 */
	private Object realService;
	private Wrapper wrapper;

	/**
	 * 在 Context 线程中执行回调
	 */
	boolean callBackInSourceContext;

	/**
	 * 创建反应式执行的代理
	 *
	 * @param vertx vertx代理
	 * @param type  类型
	 */
	public FluxInvokerProxy(Class<?> type, String address, boolean callBackInSourceContext) {
		this.type = type;
		this.address = this.getAddress(type, address);
		this.classMeta = MethodCache.set(this.type);
		this.callBackInSourceContext = callBackInSourceContext;
	}

	/**
	 * JDK 动态代理走此分支 -- 直接发起异步调用
	 */
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		return this.filter(method, args);
	}

	/**
	 * CGLIB 代理走此分支
	 */
	@Override
	public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
		return this.filter(method, args);
	}

	/**
	 * 本地的方法不进行远程调用
	 */
	private Object filter(Method method, Object[] args) throws Throwable {

		if (GET_CLASS.equals(method.getName())) {
			return type;
		}
		if (HASH_CODE.equals(method.getName())) {
			return this.hashCode();
		}
		if (TO_STRING.equals(method.getName())) {
			return type.toString();
		}
		if (EQUALS.equals(method.getName())) {
			if (args.length == 1) {
				return this.equals(args[0]);
			}
			throw new IllegalArgumentException("Invoke method [" + method.getName() + "] argument number error.");
		}

		// 方法元
		MethodMeta meta = classMeta.lookup(method);

		// 是否本地方法
		if (meta == null || meta.isLocal()) {
			throw new InvokeException("Can not invoke local method:" + method.getName());
		}

		// 非异步方法， 获取实现类来执行， 所有的都走服務接口
		if (!meta.isAsync()) {
			if (realService == null) {
				this.realService = App.getBean(this.type);
				this.wrapper = Wrapper.getWrapper(this.type);
			}
			if (realService == null) {
				realService = NONE_LOCAL_SERVICE;
			}
			if (realService == null || realService == NONE_LOCAL_SERVICE || this.wrapper == null) {
				throw new RuntimeException("Sync Request Must Define Local service.");
			}
			return wrapper.invokeMethod(realService, meta.getMethodDesc(), args);
		}

		// 发起异步调用
		// BUG：如果发起同步调用，会导致IO线程阻塞，而且回复事件也需要放入相同的IO线程中，所以会形成自己等自己的死锁
		return this.doRemoteInvoke(address, meta, args, callBackInSourceContext);
	}
}